package com.gitee.wsl.collections.set

import com.gitee.wsl.base.Objects
import com.gitee.wsl.collections.delegate.DelegatingMutableListIterator
import com.gitee.wsl.collections.delegate.DelegatingMutableSet
import com.gitee.wsl.collections.iterator.api.OrderedIterator
import com.gitee.wsl.ext.collection.isUnique


/**
 * Decorates another `Set` to ensure that the order of addition is
 * retained and used by the iterator.
 *
 *
 * If an object is added to the set for a second time, it will remain in the
 * original position in the iteration. The order can be observed from the set
 * via the iterator or toArray methods.
 *
 *
 *
 * The ListOrderedSet also has various useful direct methods. These include many
 * from `List`, such as `get(int)`,
 * `remove(int)` and `indexOf(int)`. An unmodifiable
 * `List` view of the set can be obtained via `asList()`.
 *
 *
 *
 * This class cannot implement the `List` interface directly as
 * various interface methods (notably equals/hashCode) are incompatible with a
 * set.
 *
 *
 *
 * This class is Serializable from Commons Collections 3.1.
 *
 *
 * @param <E> the type of the elements in this set
 * @since 3.0
</E> */
class ListOrderedSet<E> : DelegatingMutableSet<E> {
    /**
     * Internal iterator handle remove.
     */
    internal class OrderedSetIterator<E>(
        iterator: MutableListIterator<E>,
        /** Object we iterate on  */
        private val set: MutableCollection<E>,
    ) : DelegatingMutableListIterator<E>(iterator), OrderedIterator<E> {
        /** Last object retrieved  */
        private var last: E? = null

        override fun hasPrevious(): Boolean {
            return (delegate as ListIterator<E>).hasPrevious()
        }

        public override fun next(): E {
            last = delegate.next()
            return last!!
        }

        override fun previous(): E {
            last = (delegate as ListIterator<E>).previous()
            return last!!
        }

        public override fun remove() {
            set.remove(last)
            delegate.remove()
            last = null
        }
    }

    /** Internal list to hold the sequence of objects  */
    private val setOrder: MutableList<E>

    /**
     * Constructs a new empty `ListOrderedSet` using a
     * `HashSet` and an `ArrayList` internally.
     *
     * @since 3.1
     */
    constructor() : super(HashSet()) {
        setOrder = ArrayList()
    }

    /**
     * Constructor that wraps (not copies).
     *
     * @param set the set to decorate, must not be null
     * @throws NullPointerException if set is null
     */
    protected constructor(set: MutableSet<E>) : super(set) {
        setOrder = ArrayList(set)
    }

    /**
     * Constructor that wraps (not copies) the Set and specifies the list to
     * use.
     *
     *
     * The set and list must both be correctly initialized to the same elements.
     *
     * @param set the set to decorate, must not be null
     * @param list the list to decorate, must not be null
     * @throws NullPointerException if set or list is null
     */
    protected constructor(set: MutableSet<E>, list: MutableList<E>) : super(set) {
        setOrder = list
    }

    public override fun add(element: E): Boolean {
        if (delegate.add(element)) {
            setOrder.add(element)
            return true
        }
        return false
    }

    /**
     * Inserts the specified element at the specified position if it is not yet
     * contained in this ordered set (optional operation). Shifts the element
     * currently at this position and any subsequent elements to the right.
     *
     * @param index the index at which the element is to be inserted
     * @param object the element to be inserted
     * @see List.add
     */
    fun add(index: Int, `object`: E) {
        if (!contains(`object`)) {
            delegate.add(`object`)
            setOrder.add(index, `object`)
        }
    }

    public override fun addAll(elements: Collection< E>): Boolean {
        var result = false
        for (e in elements) {
            result = result or add(e)
        }
        return result
    }

    /**
     * Inserts all elements in the specified collection not yet contained in the
     * ordered set at the specified position (optional operation). Shifts the
     * element currently at the position and all subsequent elements to the
     * right.
     *
     * @param index the position to insert the elements
     * @param coll the collection containing the elements to be inserted
     * @return `true` if this ordered set changed as a result of the call
     * @see List.addAll
     */
    fun addAll(index: Int, coll: Collection<E>): Boolean {
        var changed = false
        // collect all elements to be added for performance reasons
        val toAdd = ArrayList<E>()
        for (e in coll) {
            if (contains(e)) {
                continue
            }
            delegate.add(e)
            toAdd.add(e)
            changed = true
        }

        if (changed) {
            setOrder.addAll(index, toAdd)
        }

        return changed
    }

    /**
     * Gets an unmodifiable view of the order of the Set.
     *
     * @return an unmodifiable list view
     */
    fun asList(): List<E> = setOrder


    public override fun clear() {
        delegate.clear()
        setOrder.clear()
    }

    /**
     * Returns the element at the specified position in this ordered set.
     *
     * @param index the position of the element in the ordered [Set].
     * @return the element at position `index`
     * @see List.get
     */
    operator fun get(index: Int): E? {
        return setOrder[index]
    }

    /**
     * Returns the index of the first occurrence of the specified element in
     * ordered set.
     *
     * @param object the element to search for
     * @return the index of the first occurrence of the object, or `-1` if
     * this ordered set does not contain this object
     * @see List.indexOf
     */
    fun indexOf(`object`: E): Int {
        return setOrder.indexOf(`object`)
    }

    public override fun iterator(): MutableIterator<E> {
        return OrderedSetIterator<E>(setOrder.listIterator(), delegate)
    }

    /**
     * Removes the element at the specified position from the ordered set.
     * Shifts any subsequent elements to the left.
     *
     * @param index the index of the element to be removed
     * @return the element that has been remove from the ordered set
     * @see List.remove
     */
    fun remove(index: Int): E? {
        val obj = setOrder.removeAt(index)
        remove(obj)
        return obj
    }

    public override fun remove(element: E): Boolean {
        val result: Boolean = delegate.remove(element)
        if (result) {
            setOrder.remove(element)
        }
        return result
    }

    public override fun removeAll(elements: Collection<E>): Boolean {
        var result = false
        for (name in elements) {
            result = result or remove(name)
        }
        return result
    }

    /**
     * @since 4.4
     */
//   fun removeIf(filter: Predicate<E>): Boolean {
//        val result: Boolean = delegate.removeIf(filter)
//        if (result) {
//            setOrder.removeIf(filter)
//        }
//        return result
//    }

    /**
     * {@inheritDoc}
     *
     *
     * This implementation iterates over the elements of this set, checking
     * each element in turn to see if it's contained in `coll`.
     * If it's not contained, it's removed from this set. As a consequence,
     * it is advised to use a collection type for `coll` that provides
     * a fast (e.g. O(1)) implementation of [Collection.contains].
     */
    public override fun retainAll(elements: Collection<E>): Boolean {
        val result: Boolean = delegate.retainAll(elements)
        if (!result) {
            return false
        }
        if (delegate.isEmpty()) {
            setOrder.clear()
        } else {
            setOrder.removeAll{ e -> !delegate.contains(e) }
        }
        return result
    }

//    public override fun toArray(): Array<Any?> {
//        return setOrder.toArray()
//    }
//
//    public override fun <T> toArray(a: Array<T?>?): Array<T?> {
//        return setOrder.toArray(a)
//    }

    /**
     * Uses the underlying List's toString so that order is achieved. This means
     * that the decorated Set's toString is not used, so any custom toStrings
     * will be ignored.
     *
     * @return a string representation of the ordered set
     */
    // Fortunately List.toString and Set.toString look the same
    override fun toString(): String {
        return setOrder.toString()
    }

    companion object {

        /**
         * Factory method to create an ordered set using the supplied list to retain order.
         *
         *
         * A `HashSet` is used for the set behavior.
         *
         *
         * NOTE: If the list contains duplicates, the duplicates are removed,
         * altering the specified list.
         *
         * @param <E> the element type
         * @param list the list to decorate, must not be null
         * @return a new ordered set
         * @throws NullPointerException if list is null
         * @since 4.0
        </E> */
        fun <E: Comparable<E>> listOrderedSet(list: List<E>): ListOrderedSet<E> {
            val set = HashSet(list)
            return ListOrderedSet(set)
        }

        /**
         * Factory method to create an ordered set.
         *
         *
         * An `ArrayList` is used to retain order.
         *
         * @param <E> the element type
         * @param set the set to decorate, must not be null
         * @return a new ordered set
         * @throws NullPointerException if set is null
         * @since 4.0
        </E> */
        fun <E> listOrderedSet(set: MutableSet<E>): ListOrderedSet<E> {
            return ListOrderedSet(set)
        }

        /**
         * Factory method to create an ordered set specifying the list and set to use.
         *
         *
         * The list and set must both be empty.
         *
         * @param <E> the element type
         * @param set the set to decorate, must be empty and not null
         * @param list the list to decorate, must be empty and not null
         * @return a new ordered set
         * @throws NullPointerException if set or list is null
         * @throws IllegalArgumentException if either the set or list is not empty
         * @since 4.0
        </E> */
        fun <E> listOrderedSet(set: MutableSet<E>, list: MutableList<E>): ListOrderedSet<E> {
            require(!(!set.isEmpty() || !list.isEmpty())) { "Set and List must be empty" }
            return ListOrderedSet(set, list)
        }
    }
}
